# frozen_string_literal: true

class RuleEntity < ApplicationRecord
  include Sortable

  module Stage
    extend ConstantEnum

    DRAFT = 'draft'
    PRODUCT = 'product'
  end

  module Status
    extend ConstantEnum

    CREATED = 'created'
    VALIDATED = 'validated'
    DEPLOYED = 'deployed'
  end

  module Policy
    extend ConstantEnum

    FIRST = 'first'
    ALL = 'all'
  end

  belongs_to :rule

  has_one :rule_schema, dependent: :destroy
  has_one :schema, through: :rule_schema

  has_many :schema_variables, through: :schema, class_name: 'SchemaVariable'
  has_many :input_variables, through: :schema, class_name: 'SchemaVariable'
  has_many :output_variables, through: :schema, class_name: 'SchemaVariable'

  has_one :rule_set_rule, dependent: :destroy
  has_one :rule_set, through: :rule_set_rule

  accepts_nested_attributes_for :rule_schema, allow_destroy: true
  accepts_nested_attributes_for :schema, allow_destroy: true

  before_create :init_status
  before_update :check_stage_must_draft

  scope :update_desc, -> { desc_by :updated_at }

  validates :stage, presence: true, inclusion: { in: Stage.values }
  validates :policy, presence: true, inclusion: { in: Policy.values }

  def fire(params)
    validate_and_raise(force: true)

    raise 'Validate inputs error.' unless schema.validate_inputs?(params)

    fire_without_validate(params)
  end

  def fire_without_validate(_params)
    []
  end

  def clone_to_draft
    return self if draft?

    transaction do
      deep_clone_to_draft
    end
  end

  def deep_clone_to_draft
    self.class.create!(
      type: type,
      rule: rule,
      policy: policy,
      stage: Stage::DRAFT,
      status: Status::CREATED,
      schema: schema
    )
  end

  def update_draft(params)
    update! params.slice(:policy)
    schema_params = params.slice(:schema_id)
    rule_schema ? rule_schema.update!(schema_params) : create_rule_schema!(schema_params)
  end

  def validate
    validate_and_raise

    update!(status: Status::VALIDATED) if status.eql?(Status::CREATED)
    true
  rescue StandardError => e
    update!(status: Status::CREATED) if status.eql?(Status::VALIDATED)
    raise e
  end

  def deploy
    return if deployed? && product?

    validate_and_raise(force: true)

    transaction do
      schema.deploy
      rule_version = rule.versions.create!(build_archive_params)
      rule.update!(current_version: rule_version.version)
      update!(stage: Stage::PRODUCT, status: Status::DEPLOYED, version: rule_version.version)
    end

    { err_code: 0, err_msg: 'Success' }
  end

  def build_archive_params
    raise 'Unsupported.'
  end

  def validate_and_raise(force: false)
    force_validate if force || status.eql?(Status::CREATED)
  end

  def deployed?
    status.eql? Status::DEPLOYED
  end

  def draft?
    !product?
  end

  def product?
    stage.eql? Stage::PRODUCT
  end

  private

  def init_status
    self.status = product? ? Status::DEPLOYED : Status::CREATED
  end

  def check_stage_must_draft
    raise 'Product的rule不能再次修改' if product?
  end

  def force_validate
    raise "schema can't be blank" if schema.nil?
  end
end
